<!--
 * @LastEditTime: 2023-11-22 11:15:19
 * @FilePath: \bdcar-net\src\views\comlbs\electricFence\index.vue
-->
<template>
  <div class="app-container">
    <!-- 地图 -->
    <div id="fence_container"></div>
    <div class="top-search">
      <poiSearch :poiSelClick='showLocatePoint' v-show="addOreditOpen"></poiSearch>
    </div>
    <div class="left-dom" id="leftTreeDom" v-show="treeShow">
      <!-- 2围栏 3线路 -->
      <ul>
        <li :class="{ active: activeType == 'all' }" @click="restData('all')">
          <span>全部</span>
          <span class="num">{{ typeCountMap.totalCount }}</span>
        </li>
        <li :class="{ active: activeType == 'fence' }" @click="restData('fence')">
          <span>围栏</span>
          <span class="num">{{ typeCountMap.polygonCount }}</span>
        </li>
        <li :class="{ active: activeType == 'line' }" @click="restData('line')">
          <span>线路</span>
          <span class="num">{{ typeCountMap.lineCount }}</span>
        </li>
      </ul>
      <div class="tree-dom" id="leftTree">
        <fenceTree :treeList="treeList" :rightMenuClick="rightMenuClick" ref="myTree" :handleClick="handleClick"
          :defaultChecked='defaultChecked'>
        </fenceTree>
      </div>
    </div>
    <div class="layer-btn" v-show="showDelBtn">
      <div class="tool-item" @click="deleteGraph()"><i class="fa fa-x fa-power-off"></i>删除
      </div>
    </div>
    <div class="right-top">
      <div class="fence-btn btn" @click="handle('polygon')" v-hasPermi="['comlbs:electricFence:addFence']">
        <el-icon>
          <Edit />
        </el-icon>
        <span>绘制围栏</span>
      </div>
      <div class="line-btn btn" @click="handle('line')" v-hasPermi="['comlbs:electricFence:addLine']">
        <el-icon>
          <Edit />
        </el-icon>
        <span>绘制线路</span>
      </div>
    </div>
    <!-- 新增 -->
    <div class="right-dom" v-if="addOreditOpen">
      <el-icon class="close-btn" @click="cancel">
        <Close />
      </el-icon>
      <div class="title">{{ title }}</div>
      <el-form ref="fenceRef" :model="form" :rules="rules" label-width="80px" style="padding: 0 20px;">
        <el-form-item v-for="v in form.type == 2 ? fenceItems : lineItems" :key="v.prop" :label="v.name" :prop="v.prop">
          <el-input v-if="v.type == 'input'" v-model.trim="form[v.prop]"></el-input>
          <el-tree-select v-else-if="v.type == 'treeSelect'" v-model="form[v.prop]" clearable :data="sel_option[v.prop]"
            :expand-on-click-node="false" :render-after-expand="false" check-strictly :props="{ label: 'label' }"
            node-key="id" filterable placeholder="" style="width: 320px;" />
          <el-input v-else-if="v.type == 'textarea'" readonly v-model="form[v.prop]" type="textarea" />
        </el-form-item>
      </el-form>
      <div class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </div>
  </div>
</template>

<script setup name="ElectricFence">
import { initMap } from '@/utils/map/leafletMap';
import { treeDept } from "@/api/system/dept";
import { fenceTreeData, saveElectricFence, getElectricFence, updateElectricFence, delElectricFence } from "@/api/comlbs/electricFence";
import { getMinAdsBy84Poi } from "@/utils/map/leafletMap"
import fenceTree from "./fenceTree"
import poiSearch from "./poiSearch.vue"
const { proxy } = getCurrentInstance();
let editType = null
let drawObj = null
let graphLayer = null //画--图层
let checkedFenceLayer = L.featureGroup() //勾选--图层组
let map = null
let inEdit = null  // 控制图层对象的编辑
let beforeLayer = L.featureGroup()
let mapSettings = JSON.parse(localStorage.getItem('basicSettings'))
const treeList = ref([])
const activeType = ref('all')
const form = ref({})
const myTree = ref(null)
const sel_option = ref({})
const addOreditOpen = ref(false)
const treeShow = ref(true)
const typeCountMap = ref({
  totalCount: 0,
  polygonCount: 0,
  lineCount: 0,
})
const title = ref('')
const fencePoints = ref([]);
// 当前树勾选的id
const defaultChecked = ref([]);
// 图形hash
let fenceHash = {};
// 绘制图层
const showDelBtn = ref(false)
const edit = ref(false) //是否可以编辑地图

const rules = ref({
  name: [
    { required: true, message: "请输入围栏名称", trigger: "blur" },
    { required: true, message: "请输入围栏名称", trigger: "change" },
  ],
  address: [
    { required: true, validator: (rule, value, callback) => { callback() }, trigger: "blur" },
  ],
  companyId: [
    { required: true, message: "请选择", trigger: "input" },
  ],
})

const fenceItems = ref([
  { name: '所属公司', prop: "companyId", type: 'treeSelect' },
  { name: '围栏名称', prop: "name", type: 'input' },
  { name: '围栏地点', prop: "address", type: 'textarea' },
])
const lineItems = ref([
  { name: '所属公司', prop: "companyId", type: 'treeSelect' },
  { name: '线路名称', prop: "name", type: 'input' },
  { name: '线路地点', prop: "address", type: 'textarea' },
])
treeDept().then(res => {
  sel_option.value.companyId = res.data
})
onMounted(() => {
  //dom生成以后才能挂载地图
  initMap("fence_container").then(_map => {
    map = _map
    initDrawCtrl()
    // 创建图层组
    checkedFenceLayer.addTo(map);
    setViewMap()
  })
  getFenceTree('')
});
function setViewMap() {
  // 基础设置中设置了地图中心点,即定位到此;否则定位到武汉
  if (mapSettings.latitude && mapSettings.longitude) {
    map.setView([mapSettings.latitude, mapSettings.longitude]);
  }
}
function getFenceTree(type, clearMap = true) {
  if (clearMap) {
    defaultChecked.value = [];
    checkedFenceLayer?.clearLayers();
  }
  //获取公司下拉树
  fenceTreeData({ type: type }).then(res => {
    treeList.value = res.data.list;
    for (let key in typeCountMap.value) {
      typeCountMap.value[key] = res.data[key]
    }
  })
}
function initDrawCtrl() {
  map.on(L.Draw.Event.CREATED, drawCreateBack);
  map.on('mouseup', (e) => {
    if (showDelBtn.value) detailSavedData()
  })
}
// type 3线路 2围栏
function restData(type) {
  activeType.value = type
  getFenceTreeByType()
}
// activeType与type的对应关系
function getFenceTreeByType(_bool = false){
  if (activeType.value == 'line') getFenceTree(3, _bool)
  else if (activeType.value == 'fence') getFenceTree(2, _bool)
  else getFenceTree('', _bool)
}

function handle(type) {
  addOreditOpen.value = true
  treeShow.value = false
  form.value = {}
  editType = type;
  switch (type) {
    case "polygon":
      title.value = '新增围栏'
      form.value.type = 2;
      break
    case "line":
      title.value = '新增线路'
      form.value.type = 3
      break
  }
  startDraw()
}

function editGraph() {
  // edit.value = !edit.value;
  // if (edit.value) {
  //   inEdit.enable();
  // } else {
  //   inEdit.disable();
  //   inEdit.save();
  //   detailSavedData();
  // }
  inEdit.enable();
  showDelBtn.value = true
}
function cancelEditGraph() {
  inEdit.disable();
  showDelBtn.value = false;
}

// 根据图层类型处理数据
function detailSavedData() {
  let _list = [], _center;
  if (editType == "line") {
    _list = beforeLayer?.getLayers()[0]?.editing?.latlngs[0] ?? [];
    _center = getPolygonAreaCenter(_list.map(v => {
      return [v.lng, v.lat]
    }))
  } else if (editType == "polygon") {
    _list = beforeLayer?.getLayers()[0]?.editing?.latlngs[0][0] ?? [];
    _center = getPolygonAreaCenter(_list.map(v => {
      return [v.lng, v.lat]
    }))
  }
  fencePoints.value = _list;
  // 根据经纬度获取地址信息
  getGraphAddress(_center);
}
function deleteGraph() {
  beforeLayer && beforeLayer.clearLayers();
  form.value.address = ''
  inEdit.disable();
  showDelBtn.value = false;
  // 删除后进入开始绘制状态
  startDraw()
}
function startDraw() {
  edit.value = true
  // 画之前清除图层
  if (graphLayer) {
    graphLayer.removeFrom(map)
    graphLayer = null;
  }
  if (drawObj) {
    drawObj.disable();
    drawObj = null;
  }
  switch (editType) {
    case 'polygon': {
      drawObj = new L.Draw.Polygon(map);
      break;
    }
    case 'line': {
      drawObj = new L.Draw.Polyline(map);
      break;
    }
  }
  // 启动
  drawObj?.enable();
}
function drawCreateBack(e) {
  edit.value = false;
  drawObj.disable();
  drawObj = null;
  // let _borderColor = '#0066FF';
  let points = [], type = 0, _center = [];
  if (editType == 'line') {
    points = e.layer.getLatLngs()
    type = 3;
  } else if (editType == 'polygon') {
    points = e.layer.getLatLngs()[0];
    type = 2;
  }
  showGraph(type, points)
  fencePoints.value = points;
  _center = getPolygonAreaCenter(points.map(v => {
    return [v.lng, v.lat]
  }))
  getGraphAddress(_center)
}
// 获取质心
function getPolygonAreaCenter(points) {
  function Area(p0, p1, p2) {
    let area = 0.0;
    area = p0[0] * p1[1] + p1[0] * p2[1] + p2[0] * p0[1] - p1[0] * p0[1] - p2[0] * p1[1] - p0[0] * p2[1];
    return area / 2;
  }
  let sum_x = 0;
  let sum_y = 0;
  let sum_area = 0;

  if (points.length == 2) {
    return [(points[0][0] + points[1][0]) / 2, (points[0][1] + points[1][1]) / 2]
  }

  let p1 = points[1],
    p2, area;
  for (let i = 2; i < points.length; i++) {
    p2 = points[i];
    area = Area(points[0], p1, p2);
    sum_area += area;
    sum_x += (points[0][0] + p1[0] + p2[0]) * area;
    sum_y += (points[0][1] + p1[1] + p2[1]) * area;
    p1 = p2;
  }
  let xx = sum_x / sum_area / 3;
  let yy = sum_y / sum_area / 3;
  return [xx, yy];
}
// 获取位置信息
function getGraphAddress(point) {
  let query = {
    lng: point[0],//城市权重优先
    lat: point[1]
  }
  getMinAdsBy84Poi(query).then(res => {
    if (res.code == 200) {
      form.value.address = res.data
    }
  })
}
function handleClick(data) {
  // 不是图形点击则返回
  if (data.polygonType === null) return;
  // 如果点击已经勾选的，直接定位
  if (defaultChecked.value.some(v => v === data.id)) {

  } else {
    // 如果点击未勾选的，定位并勾选
    myTree.value.$refs.myTree.setChecked(data.id, true);
    defaultChecked.value.push(data.id)
    fenceHash[data.id] = getFenceGraph(data);
    fenceHash[data.id].addTo(checkedFenceLayer);
  }
  map.fitBounds(fenceHash[data.id].getBounds());
}
// 树勾选
function handleCheck(data, checked) {
  function getArrayDifference(arr1, arr2) {
    const set1 = new Set(arr1);
    const set2 = new Set(arr2);
    const diff1 = arr1.filter(item => !set2.has(item));
    const diff2 = arr2.filter(item => !set1.has(item));
    return [[...diff1], [...diff2]];
  }
  const [deleteList, addList] = getArrayDifference(defaultChecked.value, checked.checkedKeys);
  defaultChecked.value = checked.checkedKeys;
  checked.checkedNodes.forEach(v => {
    if (v.polygonType !== null && addList.includes(v.id)) {
      fenceHash[v.id] = getFenceGraph(v);
    }
  })
  addList.forEach(id => {
    if (fenceHash[id]) fenceHash[id].addTo(checkedFenceLayer);
  })
  deleteList.forEach(id => {
    if (fenceHash[id]) fenceHash[id].removeFrom(checkedFenceLayer);
  })
}
function getFenceGraph(v) {
  let mapElement;
  let _borderColor = '#0066FF';
  if (v.polygonType == 2) {
    mapElement = L.polygon(v.xyWgs84, {
      fillColor: _borderColor,// 多边形填充颜色
      weight: 2, // 线条宽度，默认为 1
      color: _borderColor, // 线条颜色
    }).bindTooltip(v.label)
  } else if (v.polygonType == 3) {
    mapElement = L.polyline(v.xyWgs84, {
      fillColor: _borderColor,// 多边形填充颜色
      weight: 2, // 线条宽度，默认为 1
      color: _borderColor, // 线条颜色
    }).bindTooltip(v.label)
  }
  return mapElement;
}
// 右键编辑
function rightMenuClick(data, type) {
  if (type == 'del') {
    proxy.$modal.confirm('是否确认删除"' + data.label + '"的数据项？').then(function () {
      return delElectricFence(data.id);
    }).then(() => {
      getFenceTreeByType(true)
      proxy.$modal.msgSuccess("删除成功");
    })
  } else if (type == 'edit') {
    getElectricFence(data.id).then(res => {
      addOreditOpen.value = true
      treeShow.value = false
      title.value = '修改'
      form.value = res.data
      fencePoints.value = form.value.xyWgs84
      showGraph(data.polygonType, res.data.xyWgs84)
    })
  } else if (type == 'addFence') {
    handle('polygon')
    form.value.companyId = data.id
  } else if (type == 'addLine') {
    handle('line')
    form.value.companyId = data.id
  }
}
// 展示图层
function showGraph(type, points) {
  let _borderColor = '#0066FF';
  if (type == 2) {
    editType = 'polygon'
    graphLayer = L.polygon(points, {
      fillColor: _borderColor,// 多边形填充颜色
      weight: 2, // 线条宽度，默认为 1
      color: _borderColor, // 线条颜色
    });
  } else {
    editType = 'line'
    graphLayer = L.polyline(points, {
      fillColor: _borderColor,// 多边形填充颜色
      weight: 2, // 线条宽度，默认为 1
      color: _borderColor, // 线条颜色
    });
  }
  beforeLayer.addLayer(graphLayer).addTo(map);
  inEdit = new L.EditToolbar.Edit(map, { featureGroup: beforeLayer });
  map.fitBounds(beforeLayer.getBounds());
  // 画好图层后进入编辑状态
  editGraph()
}
function submitForm() {
  proxy.$refs["fenceRef"].validate(valid => {
    if (valid) {
      if (!form.value.address) {
        proxy.$modal.msgError('请输入线路地点')
        return
      }
      let data = {
        companyId: form.value.companyId,
        address: form.value.address,
        name: form.value.name,
        type: form.value.type,
        xyWgs84: fencePoints.value
      }
      let fun = null
      if (form.value.id) {
        data.id = form.value.id
        data.xyGcj02 = []
        data.type = form.value.type
        fun = updateElectricFence
      } else {
        fun = saveElectricFence
      }
      fun(data).then(res => {
        if (res.code == 200) {
          proxy.$modal.msgSuccess("操作成功!");
          addOreditOpen.value = false
          treeShow.value = true
          getFenceTreeByType(true)
        }
      })

      cancelEditGraph()// 取消编辑画笔
      if (graphLayer) {
        graphLayer.removeFrom(map)
        graphLayer = null;
      }
      if (checkedFenceLayer) {
        checkedFenceLayer.clearLayers();
      }
      beforeLayer && beforeLayer.clearLayers();
    }
  });
}
function cancel() {
  addOreditOpen.value = false
  treeShow.value = true
  // 初始化
  form.value = {}

  cancelEditGraph()// 取消编辑画笔
  if (drawObj) {
    drawObj.disable();
    drawObj = null;
  }
  if (graphLayer) {
    graphLayer.removeFrom(map)
    graphLayer = null;
  }
  beforeLayer && beforeLayer.clearLayers();
}
function showLocatePoint(obj) {
  map.flyTo(obj, 17, { animate: false }); //同时设置地图层级与中心点
}
defineExpose({
  handleCheck
})
</script>
<style lang='scss' scoped>
.app-container {
  margin: 0;
  width: 100%;
  height: 100%;
}

#fence_container {
  position: absolute;
  height: 100%;
  width: 100%;
  z-index: 0;
}

.top-search {
  position: absolute;
  left: 10px;
  top: 20px;
  width: 270px;
}

.left-dom {
  position: absolute;
  left: 10px;
  top: 10px;
  bottom: 10px;
  width: 270px;
  background: #fff;
  z-index: 6;

  ul {
    display: flex;
    padding: 18px 13px;
    justify-content: space-between;

    li {
      display: flex;
      width: 76px;
      height: 52px;
      padding: 0px 12px;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      border-radius: 4px;
      color: #235;
      font-family: PingFang SC;
      font-size: 14px;
      font-weight: 400;
      background: #F1F6FA;
      cursor: pointer;

      .num {
        color: #38F;
        font-weight: 600;
        margin-top: 2px;
      }
    }

    .active {
      color: #FFF;
      background: linear-gradient(90deg, #38F 0%, #00A0E9 100%);

      .num {
        color: #FFF;
      }
    }
  }

  .tree-dom {
    height: calc(100% - 100px);
    padding: 0 13px;
  }
}

.layer-btn {
  position: absolute;
  top: 20px;
  right: 385px;
  width: 70px;
  background-color: var(--el-color-primary);
  color: #fff;
  text-align: center;
  border-radius: 5px;
  z-index: 500;

  .tool-item {
    line-height: 35px;
    cursor: pointer;

    i {
      margin-right: 4px;
    }

    &:hover {
      color: #fff;
    }
  }
}

.right-top {
  position: absolute;
  top: 20px;
  right: 26px;

  .btn {
    display: flex;
    align-items: center;
    justify-content: space-around;
    padding: 4px 16px;
    border-radius: 2px;
    color: #FFF;
    text-align: center;
    font-family: PingFang SC;
    font-size: 16px;
    font-weight: 400;
    box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.12);
    cursor: pointer;

    span {
      margin-left: 5px;
    }
  }

  .fence-btn {
    background: #38F;

    &:hover {
      background: #5CA5FF;
    }
  }

  .line-btn {
    background: #11C79B;
    margin-top: 15px;

    &:hover {
      background: #2CD8AE;
    }
  }
}

.right-dom {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 360px;
  background: #FFF;
  box-shadow: -3px 0px 10px 0px rgba(0, 0, 0, 0.10);

  .close-btn {
    position: absolute;
    top: 15px;
    right: 11px;
    color: #333;
    cursor: pointer;
  }

  .title {
    color: #235;
    font-family: PingFang SC;
    font-size: 16px;
    font-weight: 600;
    margin: 20px 20px;
  }

  .el-form-item {
    display: block;
  }

  .dialog-footer {
    position: absolute;
    right: 20px;
    bottom: 20px;
  }
}
</style>